﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TYBase;

namespace TYPLC.Omron
{
    internal class OmronPLCHelper
    {
        private static byte GetMemoryCode(OmronAddressData addressData)
        {
            if (addressData.DataType == OmronDataType.Bit)
            {
                switch (addressData.PlcMemory)
                {
                    case EnumOmornPlcMemory.CIO:
                        return 0x30;
                    case EnumOmornPlcMemory.WR:
                        return 0x31;
                    case EnumOmornPlcMemory.HR:
                        return 0x32;
                    case EnumOmornPlcMemory.AR:
                        return 0x33;
                    case EnumOmornPlcMemory.DM:
                        return 0x02;
                    default:
                        return 0x00;
                }
            }
            else
            {
                switch (addressData.PlcMemory)
                {
                    case EnumOmornPlcMemory.CIO:
                        return 0xB0;
                    case EnumOmornPlcMemory.WR:
                        return 0xB1;
                    case EnumOmornPlcMemory.HR:
                        return 0xB2;
                    case EnumOmornPlcMemory.AR:
                        return 0xB3;
                    case EnumOmornPlcMemory.DM:
                        return 0x82;
                    default:
                        return 0x00;
                }
            }
        }
        public static bool AddressAs(string address, out OmronAddressData data)
        {
            data = null;
            int typeLen = -1;
            string strLen = "";
            bool isGeDigtal = false;
            for (int i = 0; i < address.Length; i++)
            {
                if (address[i] >= '0' && address[i] <= '9')
                {
                    isGeDigtal = true;
                    strLen += $"{i}";
                    continue;
                }

                if (isGeDigtal == true)
                {
                    break;
                }

            }
            typeLen = strLen.AsInt(-1);
            if (typeLen == -1)
                return false;
            data = new OmronAddressData();
            var strType = address.Left(typeLen).ToLower();
            var strAddressWithOutType = address.Right(address.Length - typeLen);

            var indexStart1 = strAddressWithOutType.IndexOf("[");
            var indexStart2 = strAddressWithOutType.IndexOf("]");
            if (indexStart1 == -1 && indexStart2 == -1)
            {
                data.IsList = false;
                data.ReadLength = 1;

            }
            else
            {
                if (indexStart1 == -1 || indexStart2 == -1)
                {
                    return false;
                }
                data.ReadLength = strAddressWithOutType.Substring(indexStart1 + 1, indexStart2 - indexStart1 - 1).Asushort();
            }
            if (indexStart1 > 0)
            {
                strAddressWithOutType = strAddressWithOutType.Substring(0, indexStart1);
            }
            if (strType == "wr")
            {
                data.PlcMemory = EnumOmornPlcMemory.WR;
            }
            else if (strType == "cio")
            {
                data.PlcMemory = EnumOmornPlcMemory.CIO;
            }
            else if (strType == "HR")
            {
                data.PlcMemory = EnumOmornPlcMemory.HR;
            }
            else if (strType == "ar")
            {
                data.PlcMemory = EnumOmornPlcMemory.AR;
            }
            else if (strType == "dm")
            {
                data.PlcMemory = EnumOmornPlcMemory.DM;
            }
            else if (strType == "d")
            {
                data.PlcMemory = EnumOmornPlcMemory.DM;
            }
            else
            {
                return false;
            }
            var indexDot = strAddressWithOutType.IndexOf(".");
            var tmps = strAddressWithOutType.Split(".");
            data.Chan = tmps[0].Asushort();
            if (tmps.Length >= 2)
            {
                data.StartBit = tmps[1].AsByte();
                data.DataType = OmronDataType.Bit;
            }
            else
            {
                data.StartBit = 0;
                data.DataType = OmronDataType.DWord;
            }
            if (data.Chan <= 0)
            {
                return false;
            }
            if (data.ReadLength <= 0)
            {
                return false;
            }
            return true;
        }



        internal static byte[] FinsCmd(OmronAddressData addressData, byte plcNode, byte pcNode,
            bool isWrite)
        {
            //byte[] array;
            //if (rw == RorW.Read)
            //    array = new byte[34];
            //else
            //    array = new byte[(int)(cnt * 2 + 33 + 1)];//长度是如何确定的在fins协议174页
            byte[] array = new byte[34];//写指令还有后面的写入数组需要拼接在一起！
            //TCP FINS header
            array[0] = 0x46;//F
            array[1] = 0x49;//I
            array[2] = 0x4E;//N
            array[3] = 0x53;//S

            array[4] = 0;//cmd length
            array[5] = 0;
            //指令长度从下面字节开始计算array[8]
            if (isWrite == false)
            {
                array[6] = 0;
                array[7] = 0x1A;//26
            }
            else
            {
                //写数据的时候一个字占两个字节，而一个位只占一个字节
                if (addressData.DataType == OmronDataType.DWord)
                {
                    array[6] = (byte)((addressData.ReadLength * 2 + 26) / 256);
                    array[7] = (byte)((addressData.ReadLength * 2 + 26) % 256);
                }
                else
                {
                    array[6] = 0;
                    array[7] = 0x1B;
                }
            }

            array[8] = 0;//frame command
            array[9] = 0;
            array[10] = 0;
            array[11] = 0x02;

            array[12] = 0;//err
            array[13] = 0;
            array[14] = 0;
            array[15] = 0;
            //command frame header
            array[16] = 0x80;//ICF
            array[17] = 0x00;//RSV
            array[18] = 0x02;//GCT, less than 8 network layers
            array[19] = 0x00;//DNA, local network

            array[20] = plcNode;//DA1
            array[21] = 0x00;//DA2, CPU unit
            array[22] = 0x00;//SNA, local network
            array[23] = pcNode;//SA1

            array[24] = 0x00;//SA2, CPU unit
            array[25] = 0xFF;
            //TODO：array[25] = Convert.ToByte(21);//SID//?????----------------------------------00-FF任意值

            //指令码
            if (isWrite == false)
            {
                array[26] = 0x01;//cmdCode--0101
                array[27] = 0x01;
            }
            else
            {
                array[26] = 0x01;//write---0102
                array[27] = 0x02;
            }
            //地址
            //array[28] = (byte)mr;
            array[28] = GetMemoryCode(addressData);
            array[29] = (byte)(addressData.Chan / 256);
            array[30] = (byte)(addressData.Chan % 256);
            array[31] = (byte)addressData.StartBit;

            array[32] = (byte)(addressData.ReadLength / 256);
            array[33] = (byte)(addressData.ReadLength % 256);

            return array;
        }


        /// <summary>
        /// （若返回的头指令为3）检查命令头中的错误代码
        /// </summary>
        /// <param name="Code">错误代码</param>
        /// <returns>指示程序是否可以继续进行</returns>
        public static bool CheckHeadError(byte Code)
        {
            switch (Code)
            {
                case 0x00: return true;
                case 0x01: return false;//RaiseException("the head is not 'FINS'");
                case 0x02: return false;//RaiseException("the data length is too long");
                case 0x03: return false;//RaiseException("the command is not supported");
            }
            //no hit
            return false;//RaiseException("unknown exception");
        }

        /// <summary>
        /// 检查命令帧中的EndCode
        /// </summary>
        /// <param name="Main">主码</param>
        /// <param name="Sub">副码</param>
        /// <returns>指示程序是否可以继续进行</returns>
        public static bool CheckEndCode(byte Main, byte Sub)
        {
            switch (Main)
            {
                case 0x00:
                    switch (Sub)
                    {
                        case 0x00: return true;//the only situation of success
                        case 0x40: return true;//错误码64，是因为PLC中产生了报警，但是数据还是能正常读到的，屏蔽64报警或清除plc错误可解决
                        case 0x01: return false;//RaiseException("service canceled");
                    }
                    break;

                case 0x01:
                    switch (Sub)
                    {
                        case 0x01: return false;//RaiseException("local node not in network");
                        case 0x02: return false;//RaiseException("token timeout");
                        case 0x03: return false;//RaiseException("retries failed");
                        case 0x04: return false;//RaiseException("too many send frames");
                        case 0x05: return false;//RaiseException("node address range error");
                        case 0x06: return false;//RaiseException("node address duplication");
                    }
                    break;

                case 0x02:
                    switch (Sub)
                    {
                        case 0x01: return false;//RaiseException("destination node not in network");
                        case 0x02: return false;//RaiseException("unit missing");
                        case 0x03: return false;//RaiseException("third node missing");
                        case 0x04: return false;//RaiseException("destination node busy");
                        case 0x05: return false;//RaiseException("response timeout");
                    }
                    break;

                case 0x03:
                    switch (Sub)
                    {
                        case 0x01: return false;//RaiseException("communications controller error");
                        case 0x02: return false;//RaiseException("CPU unit error");
                        case 0x03: return false;//RaiseException("controller error");
                        case 0x04: return false;//RaiseException("unit number error");
                    }
                    break;

                case 0x04:
                    switch (Sub)
                    {
                        case 0x01: return false;//RaiseException("undefined command");
                        case 0x02: return false;//RaiseException("not supported by model/version");
                    }
                    break;

                case 0x05:
                    switch (Sub)
                    {
                        case 0x01: return false;//RaiseException("destination address setting error");
                        case 0x02: return false;//RaiseException("no routing tables");
                        case 0x03: return false;//RaiseException("routing table error");
                        case 0x04: return false;//RaiseException("too many relays");
                    }
                    break;

                case 0x10:
                    switch (Sub)
                    {
                        case 0x01: return false;//RaiseException("command too long");
                        case 0x02: return false;//RaiseException("command too short");
                        case 0x03: return false;//RaiseException("elements/data don't match");
                        case 0x04: return false;//RaiseException("command format error");
                        case 0x05: return false;//RaiseException("header error");
                    }
                    break;

                case 0x11:
                    switch (Sub)
                    {
                        case 0x01: return false;//RaiseException("area classification missing");
                        case 0x02: return false;//RaiseException("access size error");
                        case 0x03: return false;//RaiseException("address range error");
                        case 0x04: return false;//RaiseException("address range exceeded");
                        case 0x06: return false;//RaiseException("program missing");
                        case 0x09: return false;//RaiseException("relational error");
                        case 0x0a: return false;//RaiseException("duplicate data access");
                        case 0x0b: return false;//RaiseException("response too long");
                        case 0x0c: return false;//RaiseException("parameter error");
                    }
                    break;

                case 0x20:
                    switch (Sub)
                    {
                        case 0x02: return false;//RaiseException("protected");
                        case 0x03: return false;//RaiseException("table missing");
                        case 0x04: return false;//RaiseException("data missing");
                        case 0x05: return false;//RaiseException("program missing");
                        case 0x06: return false;//RaiseException("file missing");
                        case 0x07: return false;//RaiseException("data mismatch");
                    }
                    break;

                case 0x21:
                    switch (Sub)
                    {
                        case 0x01: return false;//RaiseException("read-only");
                        case 0x02: return false;//RaiseException("protected , cannot write data link table");
                        case 0x03: return false;//RaiseException("cannot register");
                        case 0x05: return false;//RaiseException("program missing");
                        case 0x06: return false;//RaiseException("file missing");
                        case 0x07: return false;//RaiseException("file name already exists");
                        case 0x08: return false;//RaiseException("cannot change");
                    }
                    break;

                case 0x22:
                    switch (Sub)
                    {
                        case 0x01: return false;//RaiseException("not possible during execution");
                        case 0x02: return false;//RaiseException("not possible while running");
                        case 0x03: return false;//RaiseException("wrong PLC mode");
                        case 0x04: return false;//RaiseException("wrong PLC mode");
                        case 0x05: return false;//RaiseException("wrong PLC mode");
                        case 0x06: return false;//RaiseException("wrong PLC mode");
                        case 0x07: return false;//RaiseException("specified node not polling node");
                        case 0x08: return false;//RaiseException("step cannot be executed");
                    }
                    break;

                case 0x23:
                    switch (Sub)
                    {
                        case 0x01: return false;//RaiseException("file device missing");
                        case 0x02: return false;//RaiseException("memory missing");
                        case 0x03: return false;//RaiseException("clock missing");
                    }
                    break;

                case 0x24:
                    switch (Sub)
                    {
                        case 0x01: return false;//RaiseException("table missing");
                    }
                    break;

                case 0x25:
                    switch (Sub)
                    {
                        case 0x02: return false;//RaiseException("memory error");
                        case 0x03: return false;//RaiseException("I/O setting error");
                        case 0x04: return false;//RaiseException("too many I/O points");
                        case 0x05: return false;//RaiseException("CPU bus error");
                        case 0x06: return false;//RaiseException("I/O duplication");
                        case 0x07: return false;//RaiseException("CPU bus error");
                        case 0x09: return false;//RaiseException("SYSMAC BUS/2 error");
                        case 0x0a: return false;//RaiseException("CPU bus unit error");
                        case 0x0d: return false;//RaiseException("SYSMAC BUS No. duplication");
                        case 0x0f: return false;//RaiseException("memory error");
                        case 0x10: return false;//RaiseException("SYSMAC BUS terminator missing");
                    }
                    break;

                case 0x26:
                    switch (Sub)
                    {
                        case 0x01: return false;//RaiseException("no protection");
                        case 0x02: return false;//RaiseException("incorrect password");
                        case 0x04: return false;//RaiseException("protected");
                        case 0x05: return false;//RaiseException("service already executing");
                        case 0x06: return false;//RaiseException("service stopped");
                        case 0x07: return false;//RaiseException("no execution right");
                        case 0x08: return false;//RaiseException("settings required before execution");
                        case 0x09: return false;//RaiseException("necessary items not set");
                        case 0x0a: return false;//RaiseException("number already defined");
                        case 0x0b: return false;//RaiseException("error will not clear");
                    }
                    break;

                case 0x30:
                    switch (Sub)
                    {
                        case 0x01: return false;//RaiseException("no access right");
                    }
                    break;

                case 0x40:
                    switch (Sub)
                    {
                        case 0x01: return false;//RaiseException("service aborted");
                    }
                    break;
            }
            //no hit
            return false;//RaiseException("unknown exception");
        }


    }
}
